package tpase.StorageServer.Handler;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import it.unisa.dia.gas.jpbc.Pairing;
import tpase.StorageServer.DO.FileInfo;
import tpase.StorageServer.DO.UserAuth;
import tpase.StorageServer.Service.StorageServerService;
import tpase.StorageServer.VO.Message;
import tpase.StorageServer.VO.StatusCode;
import util.Util;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@ChannelHandler.Sharable
public class StorageServerStartHandler extends ChannelInboundHandlerAdapter {
    private String jpbcPath;
    private String paramPath;
    private String publicParamPath;
    private StorageServerService storageServerService;
    private Pairing pairing;


    public StorageServerStartHandler(StorageServerService storageServerService, String paramPath) {
        this.storageServerService = storageServerService;
        this.paramPath = paramPath;
        jpbcPath = paramPath + "/a.properties";
        publicParamPath = paramPath + "/publicParam.json";
        pairing = Util.getPairing(jpbcPath);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Message request = (Message) msg;
        int nowStatusCode = request.getStatusCode();

        switch (nowStatusCode) {
            case StatusCode.AUTH_STORE_REQUEST:
                authStoreResponse(ctx, request);
                break;
            case StatusCode.LOGIN_REQUEST:
                loginResponse(ctx, request);
                break;
            case StatusCode.QUERY_FILE_REQUEST:
                queryFileResponse(ctx, request);
                break;
            case StatusCode.DELETE_FILE_REQUEST:
                deleteFileResponse(ctx, request);
                break;
            case StatusCode.UPLOAD_FILE_REQUEST:
                uploadFileResponse(ctx, request);
                break;
            case StatusCode.DOWNLOAD_FILE_REQUEST:
                downloadFileResponse(ctx, request);
                break;
        }
    }

    private void downloadFileResponse(ChannelHandlerContext ctx, Message request) {
        Map<String, byte[]> map = request.getMap();
        String fid = new String(map.get("fid"));
        FileInfo fileInfo = storageServerService.getFileInfo(fid);
        byte[] bytes = Util.readFileToBytes(fileInfo.getPath());

        Message response = new Message(StatusCode.DOWNLOAD_FILE_RESPONSE);
        response.getMap().put("ciphertext", bytes);
        ctx.writeAndFlush(response);
    }

    private void uploadFileResponse(ChannelHandlerContext ctx, Message request) {
        Map<String, byte[]> map = request.getMap();
        String uid = new String(map.get("uid"));
        String fileName = new String(map.get("fileName"));
        String type = new String(map.get("type"));
        byte[] ciphertext = map.get("ciphertext");

        String fid = UUID.randomUUID().toString();
        // 用户文件夹
        String folderPathString = System.getProperty("user.dir") + "\\src\\main\\resources\\files\\" + uid + "\\";
        Path folderPath = Paths.get(folderPathString);
        // 创建 用户文件夹
        if (!Files.exists(folderPath)) {
            try {
                Files.createDirectory(folderPath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        Long size = (long) ciphertext.length;


        // 判断文件名字是否已经存在
        fileName = Util.getFileName(folderPathString, fileName);

        String filePath = folderPathString + fileName;

        FileInfo fileInfo = new FileInfo(fid, fileName, filePath, size, type, new Timestamp(System.currentTimeMillis()), uid);

        // 存入数据库
        storageServerService.storeFile(fileInfo);

        // 将 密文 存入 GlobalServer 目录下
        Util.writeBytes(ciphertext, filePath);


        Message response = new Message(StatusCode.UPLOAD_FILE_RESPONSE);
        ctx.writeAndFlush(response);

    }

    private void deleteFileResponse(ChannelHandlerContext ctx, Message request) {
        Map<String, byte[]> map = request.getMap();
        String fid = new String(map.get("fid"));
        storageServerService.deleteFile(fid);
        Message response = new Message(StatusCode.DELETE_FILE_RESPONSE);
        ctx.writeAndFlush(response);
    }

    private void loginResponse(ChannelHandlerContext ctx, Message request) {
        Map<String, byte[]> map = request.getMap();
        String uid = new String(map.get("uid"));
        byte[] userAuth1 = map.get("auth");

        Message response;
        // 该 uid 是否已经注册过
        if (!storageServerService.hasUid(uid)) {
            response = new Message(StatusCode.LOGIN_NO_UID_RESPONSE);
        } else {
            byte[] userAuth2 = storageServerService.getUserAuth(uid);
            if (Arrays.equals(userAuth1, userAuth2)) {
                response = new Message(StatusCode.LOGIN_RESPONSE);
            } else {
                response = new Message(StatusCode.LOGIN_PWD_WRONG);
            }
        }
        ctx.writeAndFlush(response);
    }

    private void authStoreResponse(ChannelHandlerContext ctx, Message request) {
        byte[] auth_uid_iBytes = request.getMap().get("auth_uid_i");
        String uid = new String(request.getMap().get("uid"));

        Message response;

        // 该 uid 是否已经注册过
        if (storageServerService.hasUid(uid)) {
            response = new Message(StatusCode.AUTH_STORE_FAIL_RESPONSE);
        } else {
            UserAuth userAuth = new UserAuth(uid, auth_uid_iBytes);
            storageServerService.storeAuth(userAuth);
            response = new Message(StatusCode.AUTH_STORE_SUCCESS_RESPONSE);
        }
        ctx.writeAndFlush(response);
    }


    private void queryFileResponse(ChannelHandlerContext ctx, Message request) {
        String uid = new String(request.getMap().get("uid"));

        List<FileInfo> fileInfoList = storageServerService.getFileInfoList(uid);

        Message message = new Message(StatusCode.QUERY_FILE_RESPONSE);
        Map<String, byte[]> map = message.getMap();

        // 将List 转 Json 字符串，再转 byte[]，Client接收到后 将 byte[] 转 Json字符串 再转 List<FileInfo>
        map.put("fileInfoList", Util.toJson(fileInfoList).getBytes());


        ctx.writeAndFlush(message);
        ctx.close();
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught: 发生异常，关闭通道");
        System.out.println(cause.toString());
        // 发生异常，关闭通道
        ctx.close();
    }

}
